﻿using System;
using System.ComponentModel;
using System.Xml.Serialization;

namespace Samurai.Wakizashi
{
    /// <summary>
    /// Represents a basic disposable class.
    /// </summary>
    public abstract class DisposableBase : IDisposable
    {
        #region Constructor/Finalizer

        /// <summary>
        /// Initializes a new instance of the <see cref="DisposableBase"/> class.
        /// </summary>
        protected DisposableBase()
        {
            IsDisposed = false;
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="DisposableBase"/> is reclaimed by garbage collection.
        /// </summary>
        ~DisposableBase()
        {
            Dispose(false);
        }

        #endregion Constructor/Finalizer

        #region Public Properties

        /// <summary>
        /// Gets a value indicating whether this instance is disposed.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
        /// </value>
        [Browsable(false)]
        [XmlIgnore]
        public bool IsDisposed { get; private set; }

        #endregion Public Properties

        #region IDisposable Members

        /// <summary>
        /// Occurs when this instance is disposed.
        /// </summary>
        public event EventHandler Disposed;

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
        public void Dispose()
        {
            if (IsDisposed) return;

            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    if (Disposed != null)
                        Disposed(this, EventArgs.Empty);
                }
            }
            finally
            {
                IsDisposed = true;
            }
        }
        #endregion IDisposable Members

        /// <summary>
        ///    <para>
        ///        Checks if the instance has been disposed of, and if it has, throws an <see cref="ObjectDisposedException"/>; otherwise, does nothing.
        ///    </para>
        /// </summary>
        /// <exception cref="ObjectDisposedException">
        ///    The instance has been disposed of.
        ///    </exception>
        ///    <remarks>
        ///    <para>
        ///        Derived classes should call this method at the start of all methods and properties that should not be accessed after a call to <see cref="Dispose()"/>.
        ///    </para>
        /// </remarks>
        protected void CheckDisposed()
        {
            if (IsDisposed) return;
            
            var typeName = GetType().FullName;

            // TODO: You might want to move the message string into a resource file
            throw new ObjectDisposedException(typeName,
                String.Format(System.Globalization.CultureInfo.InvariantCulture, "Cannot access a disposed {0}.", typeName));
            
        }
    }
}
